package com.learn.springframework.aop.support;

import com.learn.springframework.aop.aspect.Advice;
import com.learn.springframework.aop.config.AopConfig;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class AdvisedSupport {

    private AopConfig config;

    private Class<?> targetClass;

    private Pattern pointCutClassPattern;

    private Map<Method, Map<String, Advice>> methodCache;

    private Object target;


    public AdvisedSupport(AopConfig config) {
        this.config = config;
    }


    public Class<?> getTargetClass() {
        return targetClass;
    }

    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        parse();
    }

    /**
     * 解析配置文件的方法
     */
    private void parse() {
        // 权限修饰符 方法返回值、包名.类名.方法名(形参列表)
        // public .* com.learn.demo.service..*Service..*(.*)
        String pointCutRegex = this.config.getPointCut()
                .replaceAll("\\.", "\\\\.")
                .replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(")
                .replaceAll("\\)", "\\\\)");

        // public .* com.learn.demo.service..*Service..*
        String pointCutForClassRegex = pointCutRegex.substring(0, pointCutRegex.lastIndexOf("\\(") - 4);
        //com.learn.demo.service..*Service
        pointCutClassPattern = Pattern.compile("class "+pointCutForClassRegex.substring(pointCutForClassRegex.lastIndexOf(" ") + 1));
        // 保存回调通知和目标切入点方法之间的关系
        // query before() after()
        // add before()  after() afterThrowing() rallback
        try {
            // 先把切面方法缓存起来，方便解析AOP配置文件的时候可以根据方法名快速找到对应的回调方法
            Map<String, Method> aspectMethods = new HashMap<String, Method>();
            Class<?> aspectClass = Class.forName(this.config.getAspectClass());
            for (Method method : aspectClass.getMethods()) {
                aspectMethods.put(method.getName(), method);
            }

            Pattern pointCutPattern = Pattern.compile(pointCutRegex);
            for (Method method : this.targetClass.getMethods()) {
                String methodString = method.toString();
                // public void com.learn.demo.mvc.action.DemoAction.add(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse,java.lang.Integer,java.lang.Integer)

                if (methodString.contains("throws")) {
                    methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
                }
                Matcher matcher = pointCutPattern.matcher(methodString);
                if (matcher.matches()) {
                    // 执行器链
                    Map<String, Advice> advices = new HashMap<>();
                    // 把每一个方法包装成MethodIterceptor
                    // before
                    if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                        // 创建一个Advice
                        advices.put("before", new Advice(aspectClass.newInstance(), aspectMethods.get(config.getAspectBefore())));
                    }
                    // after
                    if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
                        advices.put("after", new Advice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfter())));
                    }
                    // throwWing
                    if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
                        Advice throwingAdvice = new Advice(aspectClass.newInstance(), aspectMethods.get(config.getAspectAfterThrow()));
                        throwingAdvice.setThrowName(config.getAspectAfterThrowingName());
                        advices.put("afterThrowing", throwingAdvice);
                    }
                    methodCache.put(method, advices);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Map<String, Advice> getAdvices(Method method, Class targetClass) throws Exception {
        Map<String, Advice> cache = this.methodCache.get(method);
        if (null == cache) {
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
            cache = methodCache.get(m);
            this.methodCache.put(m, cache);
        }
        return cache;
    }

    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public boolean pointCutMatch() {
        System.out.println("======this.targetClass.getName():" + this.targetClass.getName());
        System.out.println("pointCutClassPattern:" + pointCutClassPattern);
        return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }

    public Map<String, Advice> getInterceptorsAopDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws Exception {
        return getAdvices(method, targetClass);
    }

    public static void main(String[] args) {
        boolean b = Pattern.matches("a*b", "aaaab");
        System.out.println(b);
    }
}
